(* Release 3.00 *)

(*    c_coreio - low level IO stuff *)

(*    Copyright (C) 1989 Jensen and Partners. All Rights Reserved*)
(*    Written by Simon Knight.*)

(*    Copyright (C) 1991 Psion PLC. All Rights Reserved*)
(*    Written by NSM.*)

include "corelib.inc"
include "epocdefs.inc"

module coreio

(***************************************************************************)
segment _DATA(DATA, 28H)

public  __fmode : dw O_TEXT	         (* default transaltion mode *)

segment _BSS(BSS, 28H)

public  __fmask :  org 2		 (* default file permission mask *)

segment _DATA(DATA, 28H)

public  __ungotchar : dw -1		 (* temporary storage for console I/O *)

segment _DATA(DATA, 28H)

public __upperHex : db 'A'		 (* 'a' or 'A', chooses digit case *)

segment _BSS(BSS, 28H)

public __i_putch : org CodePtrSize
public __i_cgets : org CodePtrSize

segment _BSS(BSS,28H)

public __p_init : org 2

segment _BSS(BSS,28H)

public __con_key : org ConEnt

(****************************************************************************)
section
segment _TEXT(CODE,28H)
segment _DATA(DATA, 28H)

extrn __p_init
extrn __i_putch
extrn __i_cgets
extrn __d_putch
extrn __d_cgets
extrn _winHandle

select _TEXT

extrn _p_xwind

public _putch :

    cmp  byte [__p_init], 0
    jne   putchInitDone
    mov  word [__i_putch], __d_putch
    mov  word [__i_cgets], __d_cgets
    not  byte [__p_init]
    cmp	 word [_winHandle], 0
    jne	 putchInitDone
    call _p_xwind
putchInitDone:
    jmp	 word [__i_putch]

(****************************************************************************)
section
segment _TEXT(CODE,28H)
segment _DATA(DATA, 28H)

extrn _winHandle

select _TEXT

public  __d_putch :
(*
int  putch(int c)
*)

    push bx
    push cx
    push dx
    mov	 dx, 1
    mov  cx, ax
    cmp  ax, 0AH
    jne	 notNewLine
    mov  ax, 0A0DH
    inc	 dx
notNewLine:
    push cx
    push ax
    mov	 cx, sp
    mov  bx, [_winHandle]
    mov  ah, NmIoWrite
    int	 IoManager
    pop	 ax
    pop  ax
    jnc  putchExit
    mov	 ax, -1
putchExit:
    pop  dx
    pop  cx
    pop  bx
    ret  0

(****************************************************************************)
section
segment _TEXT(CODE,28H)
segment _DATA(DATA, 28H)

extrn __ungotchar
extrn _winHandle
extrn __con_key

select _TEXT

public  _getch :
(*
int  getch(void)
Checks for any character returned by ungetch then calls DOS.
*)

    mov  ax, [__ungotchar]
    cmp	 ax, -1
    jne  charWaiting
    push bx
    push cx
    push dx
    mov	 cx, __con_key
    mov	 dx, 1
    mov	 bx, [_winHandle]
    mov	 ah, NmIoRead
    int	 IoManager
    pop	 dx
    pop	 cx
    pop	 bx
    jc	 getchFailed
    mov	 ax, [__con_key][ConKeyValue]
    jmp	 charWaiting

getchFailed:
    sub	 ax, ax

charWaiting:
    mov  word [__ungotchar], -1
    ret  0

(****************************************************************************)
section
segment _TEXT(CODE,28H)

extrn _putch
extrn _getch

select _TEXT

public  _getche :
(*
int  getche(void)
Checks for any charcater returned by ungetch then calls DOS.
*)

    call _getch
    test ah, ah
    jne	 badForPutch
    jmp  near _putch
badForPutch:
    ret	 0

(****************************************************************************)
section
segment _TEXT(CODE,28H)
segment _DATA(DATA,28H)

select _DATA

extrn __ungotchar
extrn _winHandle

select _TEXT

public  _kbhit :
(*
int  kbhit(void)
Checks for any charcater waiting in keyboard buffer.
*)

    mov  ax, [__ungotchar]
    cmp	 ax, -1
    jne  keyWaiting
    push bx
    push cx
    push ax
    mov	 cx, sp
    mov	 bx, [_winHandle]
    mov	 al, IoFuncTest
    mov	 ah, NmIoWithWait
    int	 IoManager
    pop	 ax
    pop	 cx
    pop	 bx
    jnc	 keyWaiting
    sub	 ax, ax

keyWaiting:
    ret  0


(******************************************************************************)
section
segment _TEXT(CODE,28H)
segment _DATA(DATA,28H)
segment _INIT(DATA,28H)
segment _BSS(BSS,28H)

extrn __exit1
extrn __exit1_vector

select _BSS

Buf0 : org BUFSIZ
Buf1 : org BUFSIZ

select _DATA

extrn __iob
extrn __openfd
extrn __openmp
extrn __openfl
extrn _winHandle
extrn __exit_io
extrn __argin
extrn __argout
extrn __argapp
extrn __open
extrn __creat

HandleData : dw 4000H, 4002H, 6002H
StreamData : dw 405H, 0C06H, 602H
StreamBuffers : dw Buf0, Buf1, 0

public __iosetup : dw 0

select _TEXT

extrn __InitLoop

do_initio : (* defined later on ! *)

select _INIT

initio : dw MAGIC
    	 db PR2
         dw do_initio

select _DATA

public __doserrno :  dw  0, initio

select  _TEXT

extrn _p_xwind
extrn __FatalErrorPos
extrn __FatalError

public __initio :
			     (* check for character devices on default paths *)
    push di
    push si
    mov  word [__exit_io], __exit1
    cmp  word [__iosetup], 0
    je   doInitIo
    jmp  near ioDone
doInitIo:
    not  word [__iosetup]
    call _p_xwind
    sub  bx, bx
    mov	 cx, bx
    mov  si, bx
    mov  di, __iob
ioLoop:
    mov  [di][bx][_file], cx
    mov  ax, [si][StreamBuffers]
    or   ax, ax
    jz   noBuf
    mov  word [di][bx][_size], BUFSIZ
    mov  [di][bx][_base], ax
    mov  [di][bx][_ptr], ax
noBuf:
    mov  ax, [si][StreamData]
    mov  dx, [si][HandleData]
    jcxz doStdin
    cmp  cl, 1
    jne  doStdErr
doStdout:
    cmp  word [__argout], 0
    je   doDefault
    push ax
    push bx
    mov  ax, [__argout]
    cmp  word [__argapp], 0
    jne  doAppend
doCreat:
    mov  bx, FA_TEXT
    call __creat
    cmp  ax, 1
    pop  bx
    pop  ax
    je   doRestOfFlags
    jmp  initioFailed
doStdin:
    cmp  word [__argin], 0
    je   doDefault
    push ax
    push bx
    mov  ax, [__argin]
    mov  bx, O_TEXT | SH_DENYWR
    call __open
    test ax, ax
    pop  bx
    pop  ax
    je	 doRestOfFlags
    jmp  initioFailed
doDefault:
    or   dh, 20H
    or   ah, 2H
doStdErr:
    push ax
    mov  ax, [_winHandle]
    mov  [si][__openmp], ax
    pop  ax
    mov  word [si][__openfl], E_CONS | E_DEVICE
doRestOfFlags:
    mov  [di][bx][_flag], ax
    mov  [si][__openfd], dx
setDevice:
    add  bx, _iobSize
    add  si, 2
    inc  cx
    cmp  cx, 3
    je   ioDone
    jmp  ioLoop

ioDone:
    pop  si
    pop  di
    jmp  __InitLoop

doAppend:
    mov  bx, O_TEXT | O_RDWR
    call __open
    cmp  ax, 1
    mov  ax, [__argout]
    jne	 doCreat
    pop  bx
    pop  ax
    or   ax, _F_APP
    or   dx, O_APPEND
    jmp  doRestOfFlags

initioFailed:
    call __FatalErrorPos
    mov	 bx, FatalInitIo
    jmp	 __FatalError

(***************************************************************************)
section
segment _TEXT(CODE,28H)
segment _DATA(DATA, 28H)

extrn __seterrno
extrn __open_max
extrn __openmp

select _TEXT

public __getHandle :

    mov cx, [__open_max]
    mov	di, __openmp
    sub ax, ax
    cld
    repne; scasw
    jne	 noHandlesLeft
    sub	 di, __openmp+2
    ret  0
noHandlesLeft:
    mov	 ax, EMFILE
    call __seterrno
    mov	 ax, -1
    stc
    ret  0

(***************************************************************************)
section
segment _TEXT(CODE,28H)
segment _DATA(DATA, 28H)

extrn __seterrno
extrn __open_max
extrn __openmp
extrn __openfl

select _TEXT
extrn __FatalErrorPos
extrn __FatalError

public __checkHandle :
    mov  bx, ax
    cmp	 ax, [__open_max]
    jae	 invalidHandle
    sub	 ax, ax
    call __seterrno
    shl	 bx, 1
    mov	 ax, [__openfl][bx]
    mov	 bx, [__openmp][bx]
    test bx, bx
    je   invalidHandle
    test ax, E_DEVICE
    je	 notAConsol
    cmp	 bx, 1
    jne  notAConsol
    call __FatalErrorPos
    mov	 bx, FatalConIo
    jmp	 __FatalError
notAConsol:
    clc
    ret  0

invalidHandle:
    mov  ax, EBADF
    call __seterrno
    mov	 ax, -1
    stc
    ret  0

(***************************************************************************)
section
segment _TEXT(CODE,28H)
segment _DATA(DATA, 28H)

extrn __checkHandle

select  _TEXT

public  _getRealHandle :
(*
void *getRealHandle(int handle)
Get the plib handle from the clib handle.
*)

    push bx
    call __checkHandle
    mov	 ax, 0
    jc   getRealExit
    mov	 ax, bx
getRealExit:
    pop  bx
    ret  0

(***************************************************************************)
section
segment _TEXT(CODE,28H)
segment _DATA(DATA, 28H)

extrn __ioerr
extrn __getHandle
extrn __openfl
extrn __openmp

select  _TEXT

public  __creat :
public  __creat_trunc :
(*
int _creat_trunc(char *path, int permission)
Creates file or truncates existing one.
*)

    push cx
    mov  cx, ModeReplace

doCreate:
    push bx
    push dx
    push di
    mov  dx, bx
    mov  bx, ax
    push cx
    call __getHandle
    pop  cx
    jc   creatExit
    or   cx, ModeUpdate | ModeRandom
    mov  [__openfl][di], dx
    test dx, FA_TEXT
    je	 creatBinary
    or	 cx, ModeStreamText
creatBinary:
    mov  ah, NmIoOpen
    int  DevManager
    jc	 creatFailed
    mov	 [__openmp][di], ax
    mov	 ax, di
    shr  ax, 1
creatExit:
    pop  di
    pop  dx
    pop  bx
    pop  cx
    ret  0

creatFailed:
    call __ioerr
    mov  ax, -1
    jmp  creatExit

public __creat_new :
public _creatnew :
(*
int _creat_new(char *path, int permission)
Creates new file.
*)
    push cx
    mov  cx, ModeCreate
    jmp	 doCreate

public __creat_temp :
(*
int _creat_temp(char *path, int permission)
Creates new unique file.
*)

    push cx
    mov	 cx, ModeUnique
    jmp	 doCreate

(****************************************************************************)
section
segment _TEXT(CODE,28H)

extrn __creat_temp
extrn _strcpy
extrn _strlen

public _creattemp :

    push bp
    mov	 bp, sp
    sub  sp, 80H
    push bx
    push cx
    push ax
    push bx
    mov	 bx, ax
    lea	 ax, [bp][-80H]
    call _strcpy
    lea	 ax, [bp][-80H]
    call _strlen
    cmp	 ax, 3
    je	 skipCheck
    lea	 bx, [bp][-80H]
    add	 bx, ax
    cmp  byte [bx], '\'
    je	 skipCheck
    mov	 word [bx], '\'
skipCheck:
    lea	 ax, [bp][-80H]
    pop  bx
    call __creat_temp
    mov  cx, ax
    pop	 ax
    lea	 bx, [bp][-80H]
    add	 bx, 5
    call _strcpy
    mov	 ax, cx
    pop  cx
    pop  bx
    mov	 sp, bp
    pop	 bp
    ret	 0

(****************************************************************************)
section
segment _TEXT(CODE,28H)

public __exists :

    push bx
    push cx
    sub  sp, FileEnt
    mov  cx, sp
    mov  bx, ax
    sub	 al, al
    mov  ah, NmFilStatusGet
    int	 FilManager
    mov  ax, 0
    jc   existsExit
    inc	 ax
existsExit:
    add	 sp, FileEnt
    pop  cx
    pop  bx
    ret  0

(****************************************************************************)
section
segment _DATA(DATA, 28H)
segment _TEXT(CODE,28H)

extrn __ioerr
extrn __getHandle
extrn __openmp
extrn __openfl

select  _TEXT

public __open :
(*
int _open(char *path, int access)
opens file and returns handle.
*)

    push bx
    push cx
    push dx
    push di
    mov  dx, bx
    mov  bx, ax
    call __getHandle
    jc   openExit
    mov  cx, ModeOpen | ModeRandom
    mov  word [__openfl][di], 0
    test dx, O_TEXT
    je	 openBinary
    or	 cx, ModeStreamText
openBinary:
    test dx, O_RDWR
    je	 openReadonly
    or	 cx, ModeUpdate
openReadonly:
    and	 dl, 0F0H
    cmp	 dl, SH_DENYWR
    je	 allowShare
    cmp	 dl, SH_DENYNO
    jne	 noShare
allowShare:
    or	 cx, ModeShare
noShare:
    mov  ah, NmIoOpen
    int  DevManager
    jc	 openFailed
    mov	 [__openmp][di], ax
    mov	 ax, di
    shr  ax, 1
openExit:
    pop  di
    pop  dx
    pop  cx
    pop  bx
    ret  0
openFailed:
    call __ioerr
    mov  ax, -1
    jmp  openExit

(******************************************************************************)
section
segment _TEXT(CODE,28H)
segment _DATA(DATA,28H)

extrn __openmp
extrn __openfl

extrn __ioerr
extrn __checkHandle
extrn __getHandle

select  _TEXT

public __dup :
(*
int  _dup(int handle)
returns the next free file handle.
*)

    push bx
    push cx
    push dx
    push di
    mov  dx, ax
    call __checkHandle
    jc   dupFailed
    call __getHandle
    jc   dupFailed
    mov  bx, dx
    shl  bx, 1
    mov  ax, [__openmp][bx]
    mov  [__openmp][di], ax
    mov  ax, [__openfl][bx]
    mov  [__openfl][di], ax
    mov  ax, di
    shr  ax, 1
dupFailed:
    pop  di
    pop  dx
    pop  cx
    pop  bx
    ret  0

(******************************************************************************)
section
segment _TEXT(CODE,28H)
segment _DATA(DATA,28H)

extrn __open_max
extrn __openmp
extrn __openfl

select _TEXT

extrn __ioerr
extrn __checkHandle
extrn __close

public  __dup2 :
(*
int  _dup2(int oldhandle, int newhandle )
close newhandle if open and duplicates oldhandle.
*)

    push bx
    push cx
    push dx
    push di
    mov  cx, ax
    mov  dx, bx
    call __checkHandle
    jc   dup2Failed
    cmp  dx, [__open_max]
    jae  dup2Failed
    mov  di, dx
    shl  di, 1
    cmp  word [__openmp][di], 0
    je   available
    mov  ax, dx
    call __close
available:
    mov  bx, cx
    shl  bx, 1
    mov  ax, [__openmp][bx]
    mov  [__openmp][di], ax
    mov  ax, [__openfl][bx]
    mov  [__openfl][di], ax
    sub  ax, ax
dup2Failed:
    pop  di
    pop  dx
    pop  cx
    pop  bx
    ret  0

(*************************************************************************)
section
segment _TEXT(CODE,28H)
segment _DATA(DATA, 28H)

_1bs : db 08H,20H,08H
_2bs : db 08H,08H,20H,20H,08H,08H

extrn __ioerr
extrn __open_max
extrn __openfl
extrn __openmp
extrn _winHandle
extrn __checkHandle
extrn _getch
extrn _putch

select  _TEXT

public __read :
(*
int _read(int handle, void *buffer, int num)
reads num bytes from file handle int buffer.
Returns number of bytes read or -1 for error.
*)

    push bx
    push cx
    push dx
    push di
    push si
    mov	 di, cx
    mov	 si, cx
    jcxz readComplete
    mov  cx, bx
    call __checkHandle
    jc	 readExit
    test ax, E_CONS
    jne	 readConsole
readLoop:
    mov	 dx, di
    cmp	 dx, FileMaxStreamSize
    jbe  inRange
    mov	 dx, FileMaxStreamSize
inRange:
    push dx
    mov	 ah, NmIoRead
    int	 IoManager
    pop  dx
    jc   readError
    cmp	 ax, dx
    jne	 readShort
    add	 cx, dx
    sub	 di, dx
    jne	 readLoop
readComplete:
    mov	 ax, si
    sub  ax, di
readExit:
    pop  si
    pop  di
    pop  dx
    pop  cx
    pop  bx
    ret  0

readError:
    mov  di, si
    cmp	 al, EofErr
    je	 readComplete
    call __ioerr
    mov	 ax, -1
    jmp	 readExit

readShort:
    sub	 di, ax
    jmp	 readComplete

readConsole:
    cld
    xchg cx, di
readCLoop:
    call _getch
    test ah, ah
    jne	 readCLoop
    cmp	 al, 0DH
    jne	 tryBS
    mov	 al, 0AH
    call _putch

saveAndComplete:
    mov	 ax, 0A0DH
    stosw
    dec	 cx
    dec  cx
    jmp	 readCComplete

tryBS:
    cmp	 al, 08H
    jne  gotChar
    cmp  cx, si
    je   readCLoop
    dec  di
    inc  cx
    push bx
    push cx
    push dx
    mov	 bx, [_winHandle]
    mov  cx, _1bs
    mov	 dx, 3
    cmp  byte [di], ' '
    jae	 notControl
    mov	 cx, _2bs
    mov	 dl, 6
notControl:
    mov	 ah, NmIoWrite
    int	 IoManager
    pop	 dx
    pop	 cx
    pop  bx
    jmp	 readCLoop

gotChar:
    cmp	 al, ' '
    jae	 outNotControl
    push ax
    mov  al, '^'
    call _putch
    pop  ax
    push ax
    add  al, '@'
    call _putch
    pop  ax
    jmp	 saveAndContinue
outNotControl:
    call _putch
saveAndContinue:
    stosb
    loop readCLoop

readCComplete:
    xchg cx, di
    jmp	 readComplete
    

(*************************************************************************)
section
segment _TEXT(CODE,28H)
segment _DATA(DATA, 28H)

extrn _errno
extrn __openfd

select  _TEXT

extrn __read
extrn __ioerr
extrn _lseek
extrn __write

public _write :

temp       = -2
appbuf	   = -4
writesize  = -6
flag       = -7
reset      = -8
handle     = -10
buffer     = -12
num        = -14

locals     =  14

WRITE_BUF_SIZE = 400H

    push bp
    mov  bp, sp
    sub  sp, locals
    push dx
    push di
    push si
    mov  [bp][handle], ax
    mov  [bp][buffer], bx
    mov  [bp][num], cx

    mov  byte [bp][reset], 0
    mov  bx, [bp][handle]
    shl  bx, 1
    mov  cx, [__openfd][bx]
    jcxz near writeInvalid
    test cx, O_RDWR+O_WRONLY            (* test for correct access mode *)
    je   near writeAccess
    cmp  word [bp][num], 0
    je   near writeEOF                  (* don't write zero bytes *)
    test cx, O_APPEND
    je   dontAppend
    mov  di, cx                         (* append to EOF if flag set *)
    sub  ax, ax
    mov  dx, SEEK_END
    test cx, O_TEXT
    jz   appNotText
    dec  ax
    mov  cx, ax
    mov  bx, ax
    mov  ax, [bp][handle]
    call _lseek
    cmp  dx, -1
    je   nextSeek
    cmp  ax, -1
    je   nextSeek
    mov  ax, [bp][handle]
    mov  cx, 1
    lea  bx, [bp][appbuf]
    call __read
    cmp  word [_errno], 0
    jne  near writeError
    mov  ax, -1
    mov  dx, SEEK_END
    cmp  byte [bp][appbuf], CTLZ
    je   ctlzPresent
    inc  ax
ctlzPresent:
    jmp  appNotText
nextSeek:
    mov  dx, SEEK_END
    sub  ax, ax
appNotText:
    mov  cx, ax
    mov  bx, ax
    mov  ax, [bp][handle]
    call _lseek
appendDone:
    mov  cx, di
dontAppend:
    test cx, O_TEXT
    je   near writeBinary
writeText:                              (* test mode *)
    test cx, _O_EOF                     (* error if EOF flag set *)
    jne  near writeEOF
    mov  cx, WRITE_BUF_SIZE + 2
    mov	 ah, NmHeapAllocateCell
    int	 HeapManager
    jc	 near writeNomem
    inc  ax
    mov  [bp][temp], ax
    not  byte [bp][reset]
    shr  cx, 1                          (* write size is half that of buffer *)
    dec	 cx
    mov  [bp][writesize], cx
    mov  si, [bp][buffer]
writeMainLoop:                          (* main loop - translate and *)
    mov  cx, [bp][num]                  (* write a block *)
    jcxz allWritten
    cmp  cx, [bp][writesize]            (* exit when all written *)
    jb   cxRight
    mov  cx, [bp][writesize]
cxRight:
    sub  [bp][num], cx
    mov  di, [bp][temp]
    mov  ah, 0AH
    mov  [di][-1], ah                (* Dummy at buffer-1 *)
    mov  bh, CTLZ
    mov  bl, 0DH
                                        (* this next loop is the time *)
writeTransLoop:                         (* one where transaltion takes place *)
    lodsb
    cmp  al, ah
    je   doTrans                        (* no jump for no translate *)
doneTrans:
    stosb
    cmp  al, bh
    je   writeCtlz                      (* ctl Z - terminate output *)
    dec  cx
    jcxz writeBlock                     (* repeat loop body *)
    lodsb
    cmp  al, ah
    je   doTrans
    stosb
    cmp  al, bh
    je   writeCtlz
    loop writeTransLoop
    jmp  writeBlock
doTrans:
    cmp  [di][-1], bl                (* CR inserted here *)
    je   doneTrans
    mov  [di], bl
    inc  di
    jmp  doneTrans

writeCtlz:
    mov  byte [bp][flag], 1
    jmp  writeWrite
writeBlock:
    mov  byte [bp][flag], 0             (* write transalted block *)
writeWrite:
    push cx
    mov  ax, [bp][handle]
    mov  bx, [bp][temp]
    sub  di, bx
    mov  cx, di
    call __write
    pop  cx
    cmp  word [_errno], 0
    jne  writeError                     (* write error *)
writeOK:
    cmp  byte [bp][flag], 0
    je   writeMainLoop                  (* go back for another block *)
allWritten:
    mov  bx, [bp][handle]
    shl  bx, 1
    cmp  byte [bp][flag], 0
    je   dontSet
    test word [__openfd][bx], O_DEVICE  (* if !device and ctl Z set EOF flag *)
    jne  dontSet
    or   word [__openfd][bx], _O_EOF
dontSet:
    sub  si, [bp][buffer]
    jmp  writeDone
writeBinary:                            (* binary output done here *)
    mov  ax, [bp][handle]
    mov  cx, [bp][num]
    mov  bx, [bp][buffer]
    call __write
    cmp	 word [_errno], 0
    jne  writeError
    mov  si, ax
    mov	 bx, [bp][handle]
    shl  bx, 1
writeDone:
    or   word [__openfd][bx], O_CHANGED (* set file changed flag *)
writeEnd:
    cmp  byte [bp][reset], 0
    je   writeNoCell
    mov	 bx, [bp][temp]
    dec	 bx
    mov  ah, NmHeapFreeCell
    int  HeapManager
writeNoCell:
    mov  ax, si                         (* return *)
    pop  si
    pop  di
    pop  dx
    mov  sp, bp
    pop  bp
    ret  0

writeError:                             (* error return *)
    mov  si, -1
    mov  bx, ax
    jmp  writeSetErr
writeEOF:                               (* EOF return *)
    sub  si, si
    jmp  writeEnd
writeAccess:                            (* invalid access mode *)
    mov  si, -1
    mov  bx, EACCESS
    jmp  writeSetErr
writeInvalid:                           (* bad file *)
    mov  si, -1
    mov  bx, EBADF
    jmp  writeSetErr
writeNomem:                             (* no memory for buffer *)
    mov  bx, ENOMEM
writeSetErr:                            (* set errno *)
    mov  ax, bx
    call __ioerr                            (* set errno *)
    jmp  writeEnd

(*************************************************************************)
section
segment _TEXT(CODE,28H)

extrn _errno
extrn __openfd
extrn __open_max
extrn __read
extrn __ioerr

select  _TEXT

public _read :

handle  = -2
buffer  = -4
num     = -6
locals  =  6

    push bp
    mov  bp, sp
    sub  sp, locals
    push dx
    push di
    push si
    mov  [bp][handle], ax
    mov  [bp][buffer], bx
    mov  [bp][num], cx
    mov  bx, [bp][handle]
    shl  bx, 1
    mov  cx, [__openfd][bx]
    jcxz near readInvalid
    test cx, O_WRONLY
    jne  near readAccess                    (* access mode invalid *)
    cmp  word [bp][num], 0                  (* read zero - do nothing *)
    je   near readEOF
    test cx, O_TEXT
    je   near readBinary
readText:                                   (* test mode *)
    test cx, _O_EOF
    jne  near readEOF                       (* error - EOF flag set *)
    mov  cx, [bp][num]
    mov  bx, [bp][buffer]
    mov	 di, bx
    mov  ax, [bp][handle]
    call __read
    cmp	 word [_errno], 0
    jne  near readError
readOK:
    mov  cx, ax
    jcxz readCtlz
    cmp  cx, 1
    je   readOne
readOneContinue:
    mov  ah, 0DH
    mov  bh, CTLZ
    mov  si, [bp][buffer]
readTransLoop:                               (* translation loop *)
    lodsb
    cmp  al, ah
    je   missCR                              (* skip LF *)
    stosb
    cmp  al, bh
    je   readCtlz                            (* hit ctl Z - terminate input *)
missCR:
    loop readTransLoop
    jmp  readDone
readCtlz:
    mov  bx, [bp][handle]
    shl  bx, 1
    test word [__openfd][bx], O_DEVICE       (* if !device + ctl Z set EOF *)
    jne  dontSet
    or   word [__openfd][bx], _O_EOF
    jcxz dontSet
    dec  di
dontSet:
    jmp  readDone
readBinary:                                  (* binary read done here *)
    mov  ax, [bp][handle]
    mov  cx, [bp][num]
    mov  bx, [bp][buffer]
    call __read
    jmp  readExit
readDone:
    sub  di, [bp][buffer]
readEnd:
    mov  ax, di
readExit:
    pop  si
    pop  di
    pop  dx
    mov  sp, bp
    pop  bp
    ret  0

readError:                                    (* return error *)
    mov  di, -1
    mov  bx, ax
    jmp  readSetErr
readEOF:                                      (* return EOF *)
    sub  di, di
    jmp  readEnd
readAccess:                                   (* invbalid access mode *)
    mov  di, -1
    mov  bx, EACCESS
    jmp  readSetErr
readInvalid:                                  (* file not open *)
    mov  di, -1
    mov  bx, EBADF
readSetErr:                                   (* set errno *)
    mov  ax, bx
    call __ioerr                            (* set errno *)
    jmp  readEnd

readOne:
    cmp  word [bp][num], 1
    je   readExit
    jmp  readOneContinue

(*************************************************************************)
section
segment _DATA(DATA, 28H)
segment _TEXT(CODE,28H)

extrn __ioerr
extrn __open_max
extrn __openfl
extrn __openmp
extrn __checkHandle

select  _TEXT

public __write :
(*
int _write(int handle, void *buffer, int num)
writes num bytes to file handle from buffer.
Returns number of bytes written or -1 for error.  Low level DOS function.
*)

    push bx
    push cx
    push dx
    push di
    push si
    push bp
    mov	 di, cx
    mov	 si, cx
    mov  cx, bx
    call __checkHandle
    jc	 writeExit
    mov	 bp, FileMaxStreamSize
    test ax, E_DEVICE
    je	 writeLoop
    mov	 bp, 128
writeLoop:
    mov	 dx, di
    cmp	 dx, bp
    jbe  inRange
    mov	 dx, bp
inRange:
    mov	 ah, NmIoWrite
    int	 IoManager
    jc   writeError
    add	 cx, dx
    sub	 di, dx
    jne	 writeLoop
    mov	 ax, si
writeExit:
    pop	 bp
    pop  si
    pop  di
    pop  dx
    pop  cx
    pop  bx
    ret  0

writeError:
    call __ioerr
    mov	 ax, -1
    jmp	 writeExit

(***************************************************************************)
section
segment _DATA(DATA, 28H)
segment _TEXT(CODE,28H)

extrn __ioerr
extrn __openmp
extrn __openfl
extrn __open_max

select  _TEXT

extrn __checkHandle
extrn __FsChanToName
extrn __chmod
extrn __seterrno

public __close :
(*
int _close(int handle)
closes file and returns 0 if successful. Low level DOS function.
*)

    push bp
    mov	 bp, sp
    sub	 sp, 80H
    push bx
    push cx
    push di
    mov  di, ax


    mov  bx, ax
    cmp	 ax, [__open_max]
    jae	 closeInvalidHandle
    sub	 ax, ax
    call __seterrno
    shl	 bx, 1
    mov	 ax, [__openfl][bx]
    mov	 bx, [__openmp][bx]
    test bx, bx
    je   closeInvalidHandle
    test ax, E_DEVICE
    je	 closeNotAConsol
    cmp	 bx, 1
    je	 doNoClose
closeNotAConsol:
    call anyOtherHandles
doNoClose:
    mov	 ax, 1
    je	 skipClose
    push bx
    mov	 ax, bx
    lea	 bx, [bp][-80H]
    call __FsChanToName
    pop	 bx
    mov	 ah, NmIoClose
    int	 IoManager
    jc   closeFailed
    sub  ax, ax
skipClose:
    mov  word [di][__openmp], 0
    test ax, ax
    jne	 closeExit
    mov	 ax, [di][__openfl]
    test ax, E_DEVICE
    jne	 closeExit
    and  ax, FA_RDONLY | FA_HIDDEN | FA_SYSTEM
    je	 closeExit
    mov	 cx, ax
    mov	 bx, 1
    lea	 ax, [bp][-80H]
    call __chmod
    sub	 ax, ax
closeExit:
    pop  di
    pop  cx
    pop  bx
    mov	 sp, bp
    pop	 bp
    ret  0

closeInvalidHandle:
    mov  ax, EBADF
    call __seterrno
    mov	 ax, -1
    stc
    jmp closeExit

closeFailed:
    call __ioerr
    mov	 ax, -1
    jmp	 skipClose

anyOtherHandles:
    push cx
    push si
    shl	 di, 1
    mov  cx, [__open_max]
    sub  si, si
anyLoop:
    cmp	 si, di
    je   notThisOne
    cmp  [__openmp][si], bx
    je	 gotOne
notThisOne:
    inc	 si
    inc	 si
    loop anyLoop
    inc	 cx
gotOne:
    pop  si
    pop  cx
    ret  0

(****************************************************************************)
section
segment _DATA(DATA,28H)
segment _TEXT(CODE,28H)

extrn __ioerr
extrn __openfl
extrn __checkHandle

select  _TEXT

public __eof :
(*
int  _eof(int handle)
returns file status - EOF 1, error -1, else 0.
*)

    push bx
    push cx
    push dx
    push di
    push si
    mov	 di, ax
    call __checkHandle
    jc	 eofExit
    sub  ax, ax
    shl	 dx, 1
    test word [di][__openfl], E_DEVICE
    jne	 eofExit
    push ax
    push ax
    mov  di, sp
    push ax
    push ax
    mov	 dx, sp
    mov  cx, SeekFromCurrent
    mov	 ah, NmIoSeek
    int	 IoManager
    jc	 eofFailed
    mov	 dx, di
    mov  cx, SeekFromEnd
    mov	 ah, NmIoSeek
    int	 IoManager
    jc	 eofFailed
    mov	 si, sp
    sub  dx, dx
    mov  ax, [si][2]
    cmp	 [di][2], ax
    jne	 notEof
    mov  ax, [si]
    cmp	 [di], ax
    jne	 notEof
    inc	 dx
notEof:
    mov	 [di], dx
    mov	 dx, si
    mov  cx, SeekFromStart
    mov	 ah, NmIoSeek
    int	 IoManager
    jc	 eofFailed
    mov	 ax, [di]
eofExitPop:
    add  sp, 8
eofExit:
    pop  si
    pop  di
    pop  dx
    pop  cx
    pop  bx
    ret  0

eofFailed:
    call __ioerr
    mov	 ax, -1
    jmp	 eofExitPop

(******************************************************************************)
section
segment _TEXT(CODE,28H)
segment _DATA(DATA, 28H)

extrn __openfl

select _TEXT

extrn __checkHandle

public	_isatty :
(*
int	isatty (int handle)
The return value is true (1) if handle is attached to a character device
such as the console or printer,  or false (0) if handle is for an ordinary
file or is not valid.
*)

    push bx
    push cx
    push dx
    push di
    mov	 di, ax
    call __checkHandle
    jc	 isattyExit
    sub  ax, ax
    shl	 di, 1
    test word [di][__openfl], E_DEVICE
    je	 isattyExit
    inc	 ax
isattyExit:
    pop  di
    pop  dx
    pop  cx
    pop  bx
    ret  0

(*****************************************************************************)
section
segment _TEXT(CODE,28H)

extrn __ioerr
extrn __lseek
extrn __checkHandle

public _lseek :
(*
long lseek(int handle, long offset, int where)
positions handles file pointer and returns new offset.
*)

    push bx
    call __checkHandle
    jc	 seekError
    mov	 ax, bx
    pop	 bx
    jmp  near __lseek
seekError:
    cwd
    ret	 0

(******************************************************************************)
section
segment _TEXT(CODE,28H)
segment _DATA(DATA, 28H)

extrn __ioerr

select  _TEXT

extrn __checkHandle

public _tell :
(*
long tell(int handle)
Returns offset of file pointer.
*)

    push bx
    push cx
    sub	 bx, bx
    push bx
    push bx
    call __checkHandle
    jc   tellErr1
    mov	 cx, SeekFromCurrent
    mov	 dx, sp
    mov	 ah, NmIoSeek
    int  IoManager
    jc	 tellErr
    pop  ax
    pop  dx
tellOk:
    pop  cx
    pop  bx
    ret  0
tellErr:
    call __ioerr                            (* set errno *)
    mov  ax, -1                                  (* error return -1L *)
tellErr1:
    cwd
    add	sp, 4
    jmp tellOk

(*****************************************************************************)
section
segment _DATA(DATA, 28H)
segment _TEXT(CODE,28H)

extrn __ioerr

select  _TEXT

public _rmdir :
public _unlink :
(*
int unlink(char *filename)
Deletes file. Returns 0 if successful or -1 and sets errno.
*)

    push bx
    mov  bx, ax
    sub	 al, al
    mov  ah, NmFilDelete
    int	 FilManager
    jnc   unlinkOK
    call __ioerr                            (* set errno *)
    mov  ax, -1
    jmp  unlinkDone			     (* return -1 *)
unlinkOK:
    sub  ax, ax
unlinkDone:
    pop	 bx
    ret  0

(*****************************************************************************)
section
segment _DATA(DATA, 28H)
segment _TEXT(CODE,28H)

extrn __ioerr

select  _TEXT

public _rename :
(*
int rename(char *oldfile, char *newfile)
Renames file. Returns 0 if successful or -1 and sets errno.
*)

    push bx
    push cx
    mov  cx, bx
    mov  bx, ax
    sub	 al, al
    mov  ah, NmFilRename
    int	 FilManager
    jnc  renameOK
    call __ioerr                            (* set errno *)
    mov  ax, -1
    jmp  renameDone			     (* return -1 *)
renameOK:
    sub  ax, ax
renameDone:
    pop  cx
    pop  bx
    ret  0

(****************************************************************************)
section
segment _TEXT(CODE,28H)

extrn __doMakeAndChange

public _mkdir :
(*
int mkdir(const char *path)
makes new sub-directory.
returns 0 if sucessful or -1 if not.
errno is set to ENOENT if the path was not found or EACCES if access denied.
*)

    push si
    mov	 si, ax
    mov	 ax, NmFilMakeDirectory * 100H
    jmp	 near __doMakeAndChange

(******************************************************************************)
section
segment _TEXT(CODE,28H)
segment _DATA(DATA, 28H)

extrn __ioerr

select _TEXT

public _chdir :

(*
int chdir(const char *path)
Changes current working directory.
returns 0 if sucessful or -1 if not (errno is set to ENOENT).
*)

    push si
    mov	 si, ax
    mov	 ax, NmFilPathSet * 100H
public __doMakeAndChange :
    push di
    push bx
    sub	 sp, 80H
    mov	 di, sp
    push ax
    int	 StringCopy
    int	 StringLength
    add	 di, ax
    dec	 di
    cmp  byte [di], '\'
    je	 skipCheck
    mov	 word [di][1], '\'
skipCheck:
    pop	 ax
    mov	 bx, sp
    int	 FilManager
    jc   chError
    sub  ax, ax
    jmp  chDone
chError:
    call __ioerr                            (* set errno *)
    mov  ax, -1                 (* return -1 *)
chDone:
    add	 sp, 80H
    pop  bx
    pop	 di
    pop	 si
    ret  0

end
